package com.xxhui.slidingmenudemo.View;

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

/**
 * Created by hui on 2016/9/25.
 */

public class SlidingMenu extends FrameLayout {

    private static final String TAG = "SlidingMenu";
    private ViewDragHelper dragHelper;

    private int menuWidth;

    private View menuView;

    private View mainView;

    private Status preStatus = Status.Close; // 前一次保持的状态

    private Status status = Status.Close;

    public enum Status{
        Open , Close
    }

    private OnStatusListener listener;
    //留出一个接口
    public interface OnStatusListener {

        void statusChanged(Status status);

    }

    public SlidingMenu(Context context) {
        super(context);
        createDragView();
    }

    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        createDragView();
    }

    public SlidingMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        createDragView();
    }

    public void createDragView(){
        dragHelper = ViewDragHelper.create(this, callback);
    }

    /**
     * ViewGroup回调，加载完xml的时候调用，这里用于得到子View和menuWidth
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if(getChildCount()!=2){
            throw new IllegalArgumentException("子view的数量必须为2个");
        }
        menuView = getChildAt(0);
        mainView = getChildAt(1);
        //写xml的时候注意这里的顺序，menuview在前，下面是mainview
        menuWidth = menuView.getLayoutParams().width;
        Log.i(TAG, "onFinishInflate: 加载完毕");
    }

    /**
     * callback对象的回调实现是整个ViewDragHelper的逻辑核心
     */
    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        //指定parentView中的哪个子View可以被移动
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;//返回true任一childView都可以移动
        }

        //横向可以移动的范围
        @Override
        public int getViewHorizontalDragRange(View child) {
            return menuWidth;
        }

        /**
         *
         * @param child  相应的子View
         * @param left   水平方向上child移动的距离
         * @param dx     比较前一次的增量
         * @return       默认返回0，即水平移动0，而这个方法是控制水平移动的回调，而滑动菜单需要水平滑动，所以必须重写这个方法，令值为自己想要的
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if(child == mainView){
                if (left < 0)//控制左边界,不让它继续往左
                    return 0;
                else if(left > menuWidth)//控制右边界，不让它继续往右
                    return menuWidth;
                else
                    return left;//在两个值之间的话就返回left，渐变动态显示
            }//控制mainView不要超出边界
            if(child == menuView){
                Log.i(TAG, "clampViewPositionHorizontal: "+left);
                if (left > 0) {//因为menu的left>0就固定在0，不让它再往右
                    return 0;
                } else//小于0的时候就让它随负数的渐变的left隐藏在左边
                    return left;
                //不用控制左边，因为做不到，到了屏幕边界还能控制menu
            }
            return 0;
        }

        public void onViewPositionChanged(View changedView, int left, int top,
                                          int dx, int dy) {
            if (changedView == mainView)
                menuView.offsetLeftAndRight(dx);
            else
                mainView.offsetLeftAndRight(dx);
            invalidate();
            //Log.i(TAG, "onViewPositionChanged: ");
        }

        //拖拽结束的回调，内部实现是scroller，所以外部要实现computeScroll方法
        /**
         * @param releasedChild The captured(捕捉) child view now being released
         * @param xvel X velocity（速率） of the pointer as it left the screen in pixels per second.
         * @param yvel Y velocity of the pointer as it left the screen in pixels per second.
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            Log.i(TAG, "onViewReleased: released");
            if (releasedChild == mainView) {
                if (status == Status.Open) {//本来打开的话，就是关闭
                    close();
                    return;
                }
                if (xvel == 0
                        && Math.abs(mainView.getLeft()) > menuWidth / 2.0f) {//水平x没有速率，且大于菜单宽度的一半，就打开
                    open();
                } else if (xvel > 0) {//水平有速率就打开
                    open();
                } else {
                    close();
                }
            } else {
                if (xvel == 0
                        && Math.abs(mainView.getLeft()) > menuWidth / 2.0f) {
                    open();
                } else if (xvel > 0) {
                    open();
                } else {
                    close();
                }
            }
        }
    };

    @Override
    public void computeScroll() {
        super.computeScroll();
        //continueSettling方法设置为true，说明需要调用computeScroll方法绘制界面
        if(dragHelper.continueSettling(true)){
            ViewCompat.postInvalidateOnAnimation(this);
            //异步通知动画显示下一帧
            Log.i(TAG, "computeScroll: psot --inner");
        }
        Log.i(TAG, "computeScroll: done");
    }

    /**
     * 打开菜单
     */
    public void open(){
        /**
         * @param child Child view to capture and animate
         * @param finalLeft Final left position of child
         * @param finalTop Final top position of child
         * @return true if animation should continue through {@link #continueSettling(boolean)} calls
         * 要调用continueSettling方法，而
         */
        if(dragHelper.smoothSlideViewTo(mainView ,menuWidth ,0)){
            ViewCompat.postInvalidateOnAnimation(this);
            Log.i(TAG, "open: post--open()");
        }
        preStatus = status;
        status = Status.Open;
        if (listener != null && preStatus == Status.Close) {
            listener.statusChanged(status);
            Log.i(TAG, "open: StatusCallBack");
        }
        Log.i(TAG, "open: open()");
    }

    /**
     * 关闭菜单
     */
    public void close() {
        if (dragHelper.smoothSlideViewTo(mainView, 0, 0)) {
            ViewCompat.postInvalidateOnAnimation(this);
            Log.i(TAG, "close: post--colse()");
        }
        preStatus = status;
        status = Status.Close;
        if (listener != null && preStatus == Status.Open) {
            listener.statusChanged(status);
            Log.i(TAG, "close: statusCallBack");
        }
        Log.i(TAG, "close: close()");
    }
    /**
     * 切换菜单状态
     */
    public void toggle() {
        if (status == Status.Close) {
            open();
        } else {
            close();
        }
        Log.i(TAG, "toggle: toggle()");
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        menuView.layout(-menuWidth, 0, 0, menuView.getMeasuredHeight());
        mainView.layout(0, 0, right, bottom);
        Log.i(TAG, "onLayout: ");
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return dragHelper.shouldInterceptTouchEvent(ev);
        //事件拦截交给dragHelper处理
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        dragHelper.processTouchEvent(event);
        //触摸事件交给dragHelper处理
        return true;
    }

    public void setOnStatusListener(OnStatusListener listener) {
        this.listener = listener;
    }
}
